 
/* 
 * Copyright (C) Ignacio Lucas
 */
 
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include "timer_utils.h"

/**
	La implementacion de los timers se hace con clock_gettime usando el clock monotonic, para
	asegurar que el reloj avance siempre (http://groups.google.com/group/comp.os.linux.development.apps/browse_frm/thread/dc29071f2417f75f/c46264dba0863463?lnk=st&q=wall+time&rnum=1&pli=1)

	Los metodos de obtencion de tiempos son:

	timer_utils_get_current_time() devuelve el valor del reloj monotonico (en milisegundos).

	timer_utils_get_current_wall_clock_time() devuelve el valor en milisegundos desde el epoch.

*/

timer_absolute_t timer_utils_get_current_time()
{
#ifdef CLOCK_MONOTONIC
	struct timespec ts;
	timer_absolute_t ret;

	clock_gettime(CLOCK_MONOTONIC,&ts);

	ret = ((timer_absolute_t) (ts.tv_sec * 1000)) + ((timer_absolute_t) (ts.tv_nsec / 1000000));
	return ret;
#else
	return timer_utils_get_current_wall_clock_time();
#endif	

}

// devuelve la expresion en milisegundos de este momento.
timer_absolute_t timer_utils_get_current_wall_clock_time()
{
	struct timeval curr;
    gettimeofday(&curr, NULL);

	return ((timer_absolute_t)(curr.tv_sec * 1000)) + ((timer_absolute_t)(curr.tv_usec / 1000));
}

/******************************************
  timeval manipulation functions
******************************************/

/* return an integer greater than, equal to, or less than 0, 
   according as the timeval a is greater than, 
   equal to, or less than the timeval b. */
int compare_timeval(struct timeval *a, struct timeval *b) {
    if (a->tv_sec > b->tv_sec)
        return 1;
    else if (a->tv_sec < b->tv_sec)
        return -1;
    else if (a->tv_usec > b->tv_usec)
        return 1;
    else if (a->tv_usec < b->tv_usec)
        return -1;
    return 0;
}

/* copy timeval from src to dst */
void copy_timeval(struct timeval *dst, struct timeval *src) {
    dst->tv_sec = src->tv_sec;
    dst->tv_usec = src->tv_usec;
}

/* Perform timeval subtraction
    - "to - from = result"
    - return -1 if 'from' is larget(later) than 'to'
    - return 0 if success */
int subtract_timeval(struct timeval *to, struct timeval *from, struct timeval *result) {
    if (compare_timeval(to, from) < 0)
        return -1;
    result->tv_sec = to->tv_sec - from->tv_sec;
    result->tv_usec = to->tv_usec - from->tv_usec;
    if(result->tv_usec < 0)    {
        result->tv_sec--;
        result->tv_usec += 1000000;
    }    
    return 0;
}

void add_timeval(struct timeval *a, struct timeval *b, struct timeval *result) {
    result->tv_sec = a->tv_sec + b->tv_sec;
    result->tv_usec = a->tv_usec + b->tv_usec;
    if (result->tv_usec > 1000000) {
        result->tv_sec++;
        result->tv_usec -= 1000000;
    }
}

/* Adds 'interval_ms' to timeval 'a' and store in 'result'
    - 'interval_ms' is in milliseconds */
void add_ms_to_timeval(struct timeval *a, unsigned long interval_ms, struct timeval *result) {
    result->tv_sec = a->tv_sec + (interval_ms / 1000);
    result->tv_usec = a->tv_usec + ((interval_ms % 1000) * 1000);
    if (result->tv_usec > 1000000) {
        result->tv_usec -= 1000000;
        result->tv_sec++;
    }
}

/* convert struct timeval to ms(milliseconds) */
unsigned long int tv2ms(struct timeval *a) {
    return ((a->tv_sec * 1000) + (a->tv_usec / 1000));
}

/* convert ms(milliseconds) to timeval struct */
void ms2tv(struct timeval *result, unsigned long interval_ms) {
    result->tv_sec = (interval_ms / 1000);
    result->tv_usec = ((interval_ms % 1000) * 1000);
}

void clear_timeval(struct timeval *a) {
    a->tv_sec = 0;
    a->tv_usec = 0;
}

unsigned long int gettimeofday_ms() {
    struct timeval curr;
    gettimeofday(&curr, NULL);
    return tv2ms(&curr);
}



